Function Optimization

Problem:

We have a order with quantity of a product and its unit value. We know that for each product sold we gain 10% but, how should be its quantity if we gain 12% for each product and to have the same total.

PRODUCT QTY UNIT.V GAIN QTY_X
A 10 300 300 ?
B 20 200 400 ?
C 30 100 300 ?
D 40 400 1600 ?

In [1]:
from IPython.display import display

import pandas as pd

In [5]:
# data
data = pd.DataFrame([
    [10, 300],
    [20, 200],
    [30, 100],
    [40, 400]        
], columns=['QTY', 'UNIT.V'], 
   index=['A', 'B', 'C', 'D'])

display(data)


QTY UNIT.V
A 10 300
B 20 200
C 30 100
D 40 400

In [9]:
def gain(unit_v, qty):
    return unit_v*qty*0.1

In [17]:
data['GAIN'] = data.apply(lambda v: gain(v['UNIT.V'], v['QTY']), axis=1)
display(data)


QTY UNIT.V GAIN
A 10 300 300.0
B 20 200 400.0
C 30 100 300.0
D 40 400 1600.0

In [19]:
def gain_1_2(var_qty, unit_v):
    return unit_v*var_qty*0.12

scipy.optimize.fsolve


In [14]:
from scipy.optimize import fsolve

In [16]:
help(fsolve)


Help on function fsolve in module scipy.optimize.minpack:

fsolve(func, x0, args=(), fprime=None, full_output=0, col_deriv=0, xtol=1.49012e-08, maxfev=0, band=None, epsfcn=None, factor=100, diag=None)
    Find the roots of a function.
    
    Return the roots of the (non-linear) equations defined by
    ``func(x) = 0`` given a starting estimate.
    
    Parameters
    ----------
    func : callable ``f(x, *args)``
        A function that takes at least one (possibly vector) argument.
    x0 : ndarray
        The starting estimate for the roots of ``func(x) = 0``.
    args : tuple, optional
        Any extra arguments to `func`.
    fprime : callable(x), optional
        A function to compute the Jacobian of `func` with derivatives
        across the rows. By default, the Jacobian will be estimated.
    full_output : bool, optional
        If True, return optional outputs.
    col_deriv : bool, optional
        Specify whether the Jacobian function computes derivatives down
        the columns (faster, because there is no transpose operation).
    xtol : float, optional
        The calculation will terminate if the relative error between two
        consecutive iterates is at most `xtol`.
    maxfev : int, optional
        The maximum number of calls to the function. If zero, then
        ``100*(N+1)`` is the maximum where N is the number of elements
        in `x0`.
    band : tuple, optional
        If set to a two-sequence containing the number of sub- and
        super-diagonals within the band of the Jacobi matrix, the
        Jacobi matrix is considered banded (only for ``fprime=None``).
    epsfcn : float, optional
        A suitable step length for the forward-difference
        approximation of the Jacobian (for ``fprime=None``). If
        `epsfcn` is less than the machine precision, it is assumed
        that the relative errors in the functions are of the order of
        the machine precision.
    factor : float, optional
        A parameter determining the initial step bound
        (``factor * || diag * x||``).  Should be in the interval
        ``(0.1, 100)``.
    diag : sequence, optional
        N positive entries that serve as a scale factors for the
        variables.
    
    Returns
    -------
    x : ndarray
        The solution (or the result of the last iteration for
        an unsuccessful call).
    infodict : dict
        A dictionary of optional outputs with the keys:
    
        ``nfev``
            number of function calls
        ``njev``
            number of Jacobian calls
        ``fvec``
            function evaluated at the output
        ``fjac``
            the orthogonal matrix, q, produced by the QR
            factorization of the final approximate Jacobian
            matrix, stored column wise
        ``r``
            upper triangular matrix produced by QR factorization
            of the same matrix
        ``qtf``
            the vector ``(transpose(q) * fvec)``
    
    ier : int
        An integer flag.  Set to 1 if a solution was found, otherwise refer
        to `mesg` for more information.
    mesg : str
        If no solution is found, `mesg` details the cause of failure.
    
    See Also
    --------
    root : Interface to root finding algorithms for multivariate
    functions. See the 'hybr' `method` in particular.
    
    Notes
    -----
    ``fsolve`` is a wrapper around MINPACK's hybrd and hybrj algorithms.


In [20]:
fsolve(
    gain_1_2, [1], args=()
)


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-20-663f802954c7> in <module>()
      1 fsolve(
----> 2     gain_1_2, [1], args=()
      3 )

/home/xmn/anaconda3/lib/python3.5/site-packages/scipy/optimize/minpack.py in fsolve(func, x0, args, fprime, full_output, col_deriv, xtol, maxfev, band, epsfcn, factor, diag)
    144                'diag': diag}
    145 
--> 146     res = _root_hybr(func, x0, args, jac=fprime, **options)
    147     if full_output:
    148         x = res['x']

/home/xmn/anaconda3/lib/python3.5/site-packages/scipy/optimize/minpack.py in _root_hybr(func, x0, args, jac, col_deriv, xtol, maxfev, band, eps, factor, diag, **unknown_options)
    210     if not isinstance(args, tuple):
    211         args = (args,)
--> 212     shape, dtype = _check_func('fsolve', 'func', func, x0, args, n, (n,))
    213     if epsfcn is None:
    214         epsfcn = finfo(dtype).eps

/home/xmn/anaconda3/lib/python3.5/site-packages/scipy/optimize/minpack.py in _check_func(checker, argname, thefunc, x0, args, numinputs, output_shape)
     24 def _check_func(checker, argname, thefunc, x0, args, numinputs,
     25                 output_shape=None):
---> 26     res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))
     27     if (output_shape is not None) and (shape(res) != output_shape):
     28         if (output_shape[0] != 1):

TypeError: gain_1_2() missing 1 required positional argument: 'unit_v'

Conclusion